/*
 * OpenGLRenderView.hpp
 *
 *  Created on: May 4, 2009
 *      Author: asantos
 */
#undef CreateEvent

class OpenGLRenderView{
public:
	typedef int int_tp;
	typedef void(Initialized)();
private:
	SignalBroker& signalbroker;
	boost::signals::connection sdlinitializedconnection;
	bool initialized;
	bool openglscenenodeprocessorregistered;

	DisplayListFactory displaylistfactory;
public:
	OpenGLRenderView(SignalBroker& signalbroker):
		signalbroker(signalbroker),
		initialized(false),
		openglscenenodeprocessorregistered(false){
		Signal<SDLRenderView::Initialized>& sdlinitialized = signalbroker.CreateOrGetExistingSignal<SDLRenderView::Initialized>("/render/sdlinitialized");

		sdlinitializedconnection = sdlinitialized.connect(boost::bind(&OpenGLRenderView::Initialize, this));

		Signal<SceneGraphController::GetSelectedSceneGraphHandler>& getselectedscenegraph = signalbroker.CreateOrGetExistingSignal<SceneGraphController::GetSelectedSceneGraphHandler>("/scenegraphcontroller/getselectedscenegraph");
		getselectedscenegraph.connect(boost::bind(&OpenGLRenderView::Render, this, _1));
	}
protected:
	void Initialize(){
		try{
			Signal<Initialized>& openglinitialized = signalbroker.CreateOrGetExistingSignal<Initialized>("/render/openglinitialized");

			bool err = GLeeInit();
			if(err){
				signalbroker.InvokeSignal<OutputStreamView::LogHandler>("/log/output", std::string("Error initializing GL extension wrangler (GLEW)")+GLeeGetErrorString());
			}else{
				signalbroker.InvokeSignal<OutputStreamView::LogHandler>("/log/output", "Success initializing GL extension wrangler (GLEW)");
			}

			signalbroker.InvokeSignal<OutputStreamView::LogHandler>("/log/output", "Enabling 2D textures.");
			glEnable( GL_TEXTURE_2D );

			signalbroker.InvokeSignal<OutputStreamView::LogHandler>("/log/output", "Changing clear color.");
			glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );

			signalbroker.InvokeSignal<OutputStreamView::LogHandler>("/log/output", "Setting up viewport.");
			glViewport( 0, 0, 640, 480 );

			signalbroker.InvokeSignal<OutputStreamView::LogHandler>("/log/output", "Clearing color buffer.");
			glClear( GL_COLOR_BUFFER_BIT );

			signalbroker.InvokeSignal<OutputStreamView::LogHandler>("/log/output", "Initializing projection matrix.");
			glMatrixMode( GL_PROJECTION );
			glLoadIdentity();

			signalbroker.InvokeSignal<OutputStreamView::LogHandler>("/log/output", "Initializing orthogonal mode.");
			glOrtho(0.0f, 640, 480, 0.0f, -1000.0f, 1000.0f);

			signalbroker.InvokeSignal<OutputStreamView::LogHandler>("/log/output", "Initializing model view matrix.");
			glMatrixMode( GL_MODELVIEW );
			glLoadIdentity();

			//disconnect from sdlinitialize when done. don't want to initialize more than once
			sdlinitializedconnection.disconnect();
			initialized = true;
			signalbroker.InvokeSignal<OutputStreamView::LogHandler>("/log/output", "OpenGL initialized.");
			openglinitialized();
		}catch(SignalDoesNotExist& e){
			signalbroker.InvokeSignal<OutputStreamView::LogHandler>("/log/output", "Caught EventDoesNotExist");
		}
	}
	void Render(SceneGraph& scenegraph){
		if(initialized){
			glClear( GL_COLOR_BUFFER_BIT );

			//if opengl scene node processor is not registered, register it
			if(openglscenenodeprocessorregistered == false){
				std::list<std::string> geometrydependencies;
				scenegraph.RegisterNodeProcessor("geometry", geometrydependencies, boost::bind(&OpenGLRenderView::OpenGLNodeProcessor, this, _1));
				openglscenenodeprocessorregistered = true;
				scenegraph.VisitNodes(boost::bind(&OpenGLRenderView::OpenGLNodeProcessor, this, _1));
			}

			//render the scenegraph
			//signalbroker.InvokeSignal("/log/output", (ArgsList(),"Rendering SceneGraph with OpenGL."));
			RenderSceneNodeAndChildren(scenegraph.GetRoot());

			SDL_GL_SwapBuffers();
		}
	}

	void OpenGLNodeProcessor(SceneGraph::SceneNode& scenenode){
		//has geometry but no openglvbo?
		if(scenenode.HasSceneNodeProperty("geometry") && !scenenode.HasSceneNodeProperty("renderable")){
			signalbroker.InvokeSignal<OutputStreamView::LogHandler>("/log/output", "Processing geometry property into opengldisplaylist property.");
			//create the openglvbo from the geometry
			Geometry& geometry = scenenode.GetSceneNodeProperty<GeometryProperty>("geometry").GetGeometry();
			//PositionProperty& positionproperty scenenode.GetSceneNodeProperty<PositionProperty>("position");
			const TriangleStrip& trianglestrip = dynamic_cast<const TriangleStrip&>(geometry);
			VBOFactory vbofactory;
			//VBO vbo = vbofactory.CreateFromTriangleStrip(trianglestrip);
			//scenenode.AddSceneNodeProperty("openglvbo", new RenderableProperty(vbo));

			IRenderable& displaylist = displaylistfactory.CreateFromTriangleStrip(trianglestrip);
			scenenode.AddSceneNodeProperty("renderable", new RenderableProperty(displaylist));
		}

	}
	void RenderSceneNodeAndChildren(SceneNode& scenenode){
		if(scenenode.HasSceneNodeProperty("position")){
			const PositionProperty& positionproperty = scenenode.GetSceneNodeProperty<PositionProperty>("position");
			PushMatrix4(positionproperty.GetPosition());
		}
		RenderSceneNode(scenenode);
		const SceneNode::SceneNodes& children = scenenode.GetChildNodes();
		SceneNode::SceneNodes::const_iterator itr = children.begin();
		for(;itr!=children.end(); itr++){
			RenderSceneNodeAndChildren(**itr);
		}
		if(scenenode.HasSceneNodeProperty("position")){
			glPopMatrix();
			//signalbroker.InvokeSignal("/log/output", (ArgsList(),"Popping matrix"));
		}
	}
	void RenderSceneNode(SceneNode& scenenode){
		/*if(scenenode.HasSceneNodeProperty("openglvbo")){
			scenenode.GetSceneNodeProperty<RenderableProperty>("openglvbo").Render();
		}*/
		try{
			if(scenenode.HasSceneNodeProperty("renderable")){
				//signalbroker.InvokeSignal("/log/output", (ArgsList(),"Rendering SceneNode with OpenGL."));

				const IRenderable& renderable = scenenode.GetSceneNodeProperty<RenderableProperty>("renderable");
				renderable.Render();
			}
		}catch(SceneNodePropertyDoesNotExist e){
			signalbroker.InvokeSignal<OutputStreamView::LogHandler>("/log/output", "SceneNode does not have renderable property.");
		}
	}
private:
	void PushMatrix4(const Matrix4& m){
		GLfloat* mptr = new GLfloat[16];
		for(int i=0; i<4; i++){
			for(int j=0; j<4; j++){
				mptr[j*4+i] = m(i,j);
			}
		}
		glPushMatrix();
		glMultMatrixf(mptr);
		delete mptr;
	}
};
